package com.ssy.lingxi.order.serviceimpl.base;

import cn.hutool.json.JSONUtil;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.dto.request.PlatformSettlementOrderPaymentVO;
import com.ssy.lingxi.marketing.api.model.constant.MkFeignConstants;
import com.ssy.lingxi.marketing.api.model.constant.MkQueueMessageTypeEnum;
import com.ssy.lingxi.marketing.api.model.dto.MarketingQueueDTO;
import com.ssy.lingxi.order.api.model.constant.OrderFeignConstant;
import com.ssy.lingxi.order.api.model.queue.OrderGroupDTO;
import com.ssy.lingxi.order.api.model.queue.OrderPaymentCallbackDTO;
import com.ssy.lingxi.order.entity.OrderDO;
import com.ssy.lingxi.order.model.constant.OrderPaymentCallbackStatusEnum;
import com.ssy.lingxi.order.model.constant.OrderPromotionStatusEnum;
import com.ssy.lingxi.order.model.constant.OrderQueueMessageTypeEnum;
import com.ssy.lingxi.order.model.constant.OrderServiceContants;
import com.ssy.lingxi.order.model.dto.*;
import com.ssy.lingxi.order.service.base.IBaseOrderPaymentService;
import com.ssy.lingxi.order.service.base.IBaseOrderScheduleService;
import com.ssy.lingxi.order.service.feign.IMemberFeignService;
import com.ssy.lingxi.order.service.web.IBuyerOrderService;
import com.ssy.lingxi.order.service.web.IOrderTimeParamService;
import com.ssy.lingxi.order.service.web.IVendorOrderService;
import com.ssy.lingxi.order.utils.NumberUtil;
import com.ssy.lingxi.order.utils.OrderStringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;

/**
 * 订单相关定时任务接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-10-21
 */
@Service
public class BaseOrderScheduleServiceImpl implements IBaseOrderScheduleService {
    private final static Logger logger = LoggerFactory.getLogger(BaseOrderScheduleServiceImpl.class);

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private IOrderTimeParamService orderTimeParamService;

    @Resource
    private IBuyerOrderService buyerOrderService;

    @Resource
    private IVendorOrderService vendorOrderService;

    @Resource
    private IBaseOrderPaymentService baseOrderPaymentService;

    @Resource
    private IMemberFeignService memberFeignService;

    /**
     * 将延迟消息重新加入队列
     *
     * @param queueDTO 消息实体
     * @return 是否需要（或已经）重新加入队列
     */
    @Override
    public Boolean reScheduleDelayMessage(OrderQueueDTO queueDTO) {
        if(queueDTO == null || NumberUtil.isNullOrNegativeZero(queueDTO.getDelayTime()) || queueDTO.getDelayTime() <= (long) Integer.MAX_VALUE) {
            return false;
        }

        long leftTime = queueDTO.getDelayTime() - (long) Integer.MAX_VALUE;
        queueDTO.setDelayTime(leftTime);
        queueDTO.setTimes(queueDTO.getTimes() + 1);

        Integer delayMillSeconds = leftTime >  Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) leftTime;
        try {
            //延迟时间的单位是毫秒
            rabbitTemplate.convertAndSend(OrderServiceContants.ORDER_SERVICE_DELAY_EXCHANGE, OrderServiceContants.ORDER_SERVICE_DELAY_ROUTING_KEY, OrderStringUtil.serializeObject(queueDTO), message -> {
                message.getMessageProperties().setDelay(delayMillSeconds);
                return message;
            });
            return true;
        } catch (Exception e) {
            logger.error("重新推送至延迟消息队列错误，data: " + OrderStringUtil.serializeObject(queueDTO) + ", error:" + e.getMessage());
            return false;
        }
    }

    /**
     * 将延迟消息重新加入队列
     *
     * @param messageTypeEnum 消息类型
     * @param jsonMessage     消息实体
     * @param millSeconds     延迟时间（毫秒）
     * @param times           重新入队列次数
     */
    @Override
    public void reScheduleDelayMessage(OrderQueueMessageTypeEnum messageTypeEnum, String jsonMessage, Integer millSeconds, Integer times) {
        OrderQueueDTO queueDTO = new OrderQueueDTO();
        queueDTO.setType(messageTypeEnum.getCode());
        queueDTO.setMessage(jsonMessage);
        queueDTO.setTimes(times);
        queueDTO.setDelayTime((long) millSeconds);

        try {
            //延迟时间的单位是毫秒
            rabbitTemplate.convertAndSend(OrderServiceContants.ORDER_SERVICE_DELAY_EXCHANGE, OrderServiceContants.ORDER_SERVICE_DELAY_ROUTING_KEY, OrderStringUtil.serializeObject(queueDTO), message -> {
                message.getMessageProperties().setDelay(millSeconds);
                return message;
            });
        } catch (Exception e) {
            logger.error("重新推送至延迟消息队列错误，data: " + OrderStringUtil.serializeObject(queueDTO) + ", error:" + e.getMessage());
        }
    }

    /**
     * 延迟队列 - 推送
     *
     * @param messageType 消息类型
     * @param object      消息对象
     * @param millSeconds 延迟时间（毫秒）
     */
    @Override
    public void scheduleDelayMessage(OrderQueueMessageTypeEnum messageType, Object object, Long millSeconds) {
        //转换为Json字符串，推送至消息队列
        String json = OrderStringUtil.serializeObject(object);

        OrderQueueDTO queueDTO = new OrderQueueDTO(messageType.getCode(), json, millSeconds);
        String jsonMessage = OrderStringUtil.serializeObject(queueDTO);

        //延时时间，如果大于 Integer.MAX_VALUE，在接收消息时处理
        Integer delayMillSeconds = millSeconds > (long) Integer.MAX_VALUE ? Integer.MAX_VALUE : millSeconds.intValue();

        try {
            logger.info("推送至延迟消息队列 => " + jsonMessage);
            //延迟时间的单位是毫秒
            rabbitTemplate.convertAndSend(OrderServiceContants.ORDER_SERVICE_DELAY_EXCHANGE, OrderServiceContants.ORDER_SERVICE_DELAY_ROUTING_KEY, jsonMessage, message -> {
                message.getMessageProperties().setDelay(delayMillSeconds);
                return message;
            });
        } catch (Exception e) {
            logger.error("推送至延迟消息队列错误，data: " + json + ", error:" + e.getMessage());
        }
    }

    /**
     * （交易流程规则配置）订单自动取消 - 将订单推送至延迟队列
     *
     * @param orders 订单列表
     */
    @Override
    public void scheduleOrderCancel(List<OrderDO> orders) {
        orders.forEach(order -> scheduleOrderCancel(order.getId(), order.getTask() == null ? BigDecimal.ZERO : order.getTask().getExpireHours()));
    }

    /**
     * （交易流程规则配置）订单自动取消 - 将订单推送至延迟队列
     *
     * @param orderId    订单Id
     * @param delayHours 延迟时间
     */
    @Override
    public void scheduleOrderCancel(Long orderId, BigDecimal delayHours) {
        if(NumberUtil.isNullOrNegativeZero(orderId) || NumberUtil.isNullOrNegativeZero(delayHours)) {
            return;
        }

        long millSeconds = delayHours.multiply(BigDecimal.valueOf(60 * 60 * 1000)).longValue();
        OrderCancelDTO cancelDTO = new OrderCancelDTO(orderId);
        scheduleDelayMessage(OrderQueueMessageTypeEnum.AUTO_CANCEL, cancelDTO, millSeconds);
    }

    /**
     * 订单自动取消
     *
     * @param jsonMessage 消息队列中的消息
     */
    @Override
    public void orderCancel(String jsonMessage) {
        logger.info("订单自动取消消息 => " + jsonMessage);
        OrderCancelDTO cancelDTO = OrderStringUtil.deserializeObject(jsonMessage, OrderCancelDTO.class);
        if(cancelDTO == null || NumberUtil.isNullOrNegativeZero(cancelDTO.getOrderId())) {
            logger.error("订单自动取消反序列化消息错误 => " + jsonMessage);
            return;
        }

        Wrapper<Void> cancelResult = vendorOrderService.cancelOrder(cancelDTO.getOrderId(), OrderServiceContants.SYSTEM_NAME, "", "", OrderServiceContants.SYSTEM_NAME, "交易流程设置自动取消");
        if(cancelResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            logger.info("订单自动取消失败，jsonMessage:" + jsonMessage + ", code:" + cancelResult.getCode() + ", msg:" + cancelResult.getMessage());
        }
    }

    /**
     * 定时收货 - 将发货批次推送至延迟队列
     *
     * @param order    订单
     * @param batchNo 发货批次
     */
    @Override
    public void scheduleOrderDelivery(OrderDO order, Integer batchNo) {
        //查询供应商自动收货时间（单位：天）配置
        Integer receiveDays = orderTimeParamService.getReceiveDays(order.getVendorMemberId(), order.getVendorRoleId(), order.getShopId());
        if(NumberUtil.isNullOrNegativeZero(receiveDays)) {
            return;
        }

        //转换为Json字符串，推送至消息队列
        String json = OrderStringUtil.serializeObject(new OrderScheduleReceiveDTO(order.getId(), batchNo));

        OrderQueueDTO queueDTO = new OrderQueueDTO(OrderQueueMessageTypeEnum.AUTO_RECEIVE.getCode(), json, (long) (receiveDays * 86400000));
        String jsonMessage = OrderStringUtil.serializeObject(queueDTO);
        try {
            //延迟时间的单位是毫秒
            rabbitTemplate.convertAndSend(OrderServiceContants.ORDER_SERVICE_DELAY_EXCHANGE, OrderServiceContants.ORDER_SERVICE_DELAY_ROUTING_KEY, jsonMessage, message -> {
                message.getMessageProperties().setDelay(receiveDays * 86400000);
                return message;
            });
        } catch (Exception e) {
            logger.error("自动收货推送至消息队列错误，data: " + json + ", error:" + e.getMessage());
        }
    }

    /**
     * 定时收货 - 接收延迟队列数据，发货
     *
     * @param jsonMessage 从延迟队列中接收到的消息（订单Id和发货批次）
     */
    @Override
    public void scheduleOrderReceive(String jsonMessage) {
        try {
            OrderScheduleReceiveDTO scheduleReceive = OrderStringUtil.deserializeObject(jsonMessage, OrderScheduleReceiveDTO.class);
            if(scheduleReceive == null || NumberUtil.isNullOrZero(scheduleReceive.getOrderId()) || NumberUtil.isNullOrZero(scheduleReceive.getBatchNo())) {
                logger.error("自动收货错误，接收到的消息反序列化后数据错误：" + jsonMessage);
                return;
            }

            Wrapper<Void> result = buyerOrderService.receiveOrder(OrderServiceContants.SYSTEM_NAME, "", "", OrderServiceContants.SYSTEM_NAME, scheduleReceive.getOrderId(), scheduleReceive.getBatchNo(), "");
            logger.info("自动收货结果 => " + result.getCode() + ", " + result.getMessage());

        } catch (Exception e) {
            logger.error("自动收货错误，data:" + jsonMessage + ", error:" + e.getMessage());
        }
    }

    /**
     * 当调用营销服务接口返回拼团失败后，将拼团失败的订单重新加入Mq进行处理
     *
     * @param orderId 拼团订单Id
     * @param groupId 拼团Id（拼团失败时会为Null）
     */
    @Override
    public void scheduleGroupOrder(Long orderId, Long groupId) {
        OrderGroupDTO groupOrder = new OrderGroupDTO();
        groupOrder.setOrderId(orderId);
        groupOrder.setStatus(0);
        groupOrder.setGroupPurchaseId(groupId);
        String jsonMessage = OrderStringUtil.serializeObject(groupOrder);
        logger.info("重新将拼团失败的订单加入队列 => " + jsonMessage);
        try {
            rabbitTemplate.convertAndSend(OrderFeignConstant.ORDER_GROUP_EXCHANGE, OrderFeignConstant.ORDER_GROUP_ROUTINGKEY, jsonMessage);
        } catch (Exception e) {
            logger.info("重新将拼团失败的订单加入队列异常 => " + jsonMessage + " , msg:" + e.getMessage());
        }
    }

    /**
     * 拼团订单通知
     *
     * @param jsonMessage 从消息队列中接收到的消息（订单Id和拼团状态）
     */
    @Override
    public void updateOrderGroupStatus(String jsonMessage) {
        logger.info("接收到Mq拼团消息 => " + jsonMessage);

        OrderGroupDTO orderGroup = OrderStringUtil.deserializeObject(jsonMessage, OrderGroupDTO.class);
        if(orderGroup == null || orderGroup.getStatus() == null || NumberUtil.isNullOrZero(orderGroup.getOrderId())) {
            logger.error("拼团订单通知错误，接收到的消息反序列化后数据错误：" + jsonMessage);
            return;
        }

        OrderPromotionStatusEnum promotionStatus;
        switch (orderGroup.getStatus()) {
            case 0:
                promotionStatus = OrderPromotionStatusEnum.GROUP_FAILED;
                break;
            case 1:
                promotionStatus = OrderPromotionStatusEnum.GROUP_SUCCESS;
                break;
            default:
                logger.error("拼团订单通知错误，接收到的消息反序列化后数据错误：" + jsonMessage);
                return;
        }

        Wrapper<Void> result = buyerOrderService.updateGroupOrderStatus(OrderServiceContants.SYSTEM_NAME, "", "", OrderServiceContants.SYSTEM_NAME, orderGroup.getOrderId(), promotionStatus, orderGroup.getGroupPurchaseId());
        if(result.getCode() != ResponseCode.SUCCESS.getCode()) {
            logger.info("修改拼团订单状态错误 => orderId:" + orderGroup.getOrderId() + ", code:" + result.getCode() + ", msg:" + result.getMessage());
        }
    }

    /**
     * 结算服务 - 更新订单支付记录结算状态
     *
     * @param jsonMessage 从消息队列中接收到的消息（订单Id列表和结算状态）
     */
    @Override
    public void updateOrderPaymentStatus(String jsonMessage) {
        logger.info("接收到Mq更新订单支付记录结算状态消息 => " + jsonMessage);

        PlatformSettlementOrderPaymentVO paymentVO = OrderStringUtil.deserializeObject(jsonMessage, PlatformSettlementOrderPaymentVO.class);
        if(paymentVO == null || paymentVO.getSettlementStatus() == null || CollectionUtils.isEmpty(paymentVO.getPaymentIds())) {
            logger.error("更新订单支付记录结算状态消息反序列化后数据错误：" + jsonMessage);
            return;
        }

        Wrapper<Void> result = baseOrderPaymentService.updatePaymentSettlementStatus(paymentVO.getPaymentIds(), paymentVO.getSettlementStatus());
        if(result.getCode() != ResponseCode.SUCCESS.getCode()) {
            logger.info("更新订单支付记录结算状态 => mq_msg:" + jsonMessage + ", code:" + result.getCode() + ", msg:" + result.getMessage());
        }
    }

    /**
     * 延迟队列 - 接收到查询支付结果消息时，查询支付状态，并更新订单状态
     * @param times        加入队列的次数
     * @param jsonMessage 从消息队列中接收到的消息
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void orderQueueCallback(Integer times, String jsonMessage) {
        logger.info("接收到延迟队列查询订单支付状态消息 => " + jsonMessage);

        OrderPaymentQueueDTO queueDTO = OrderStringUtil.deserializeObject(jsonMessage, OrderPaymentQueueDTO.class);
        if(queueDTO == null || CollectionUtils.isEmpty(queueDTO.getOrderIds()) || CollectionUtils.isEmpty(queueDTO.getPaymentIds())) {
            logger.error("延迟队列查询订单支付状态消息反序列化后数据错误：" + jsonMessage);
            return;
        }

        Wrapper<OrderPaymentCallbackStatusEnum> statusResult = baseOrderPaymentService.findPaymentStatus(queueDTO.getPayChannel(), queueDTO.getTradeNo(), queueDTO.getOrderIds(), queueDTO.getPaymentIds());
        if(statusResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            //重新加入队列，10分钟后查询
            reScheduleDelayMessage(OrderQueueMessageTypeEnum.PAYMENT_QUERY, jsonMessage, 10 * 60 * 1000, times + 1);
            return;
        }

        Wrapper<Void> result = baseOrderPaymentService.buyerPayCallback(queueDTO.getOrderIds(), queueDTO.getBatchNo(), queueDTO.getTradeNo(), statusResult.getData(), queueDTO.getUserName(), queueDTO.getOrganizationName(), queueDTO.getJobTitle());
        if(result.getCode() != ResponseCode.SUCCESS.getCode()) {
            logger.error("订单查询状态后更新处理错误，code:" + result.getCode() + ", msg:" + result.getMessage());
        }
    }

    /**
     * 订单支付 - 回调通知队列
     *
     * @param jsonMessage 从消息队列中接收到的消息
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void orderPayCallback(String jsonMessage) {
        logger.info("接收到Mq订单支付回调消息 => " + jsonMessage);
        OrderPaymentCallbackDTO callbackDTO = OrderStringUtil.deserializeObject(jsonMessage, OrderPaymentCallbackDTO.class);
        if(callbackDTO == null || callbackDTO.getPayStatus() == null || !StringUtils.hasLength(callbackDTO.getAttach())) {
            logger.error("订单支付回调消息反序列化后数据错误：" + jsonMessage);
            return;
        }

        OrderPaymentCallbackStatusEnum callbackStatusEnum = OrderPaymentCallbackStatusEnum.parse(callbackDTO.getPayStatus());
        if(callbackStatusEnum == null) {
            logger.error("订单支付回调消息反序列化后数据错误：" + jsonMessage);
            return;
        }

        OrderPaymentAttachDTO attachDTO = OrderStringUtil.deserializeObject(callbackDTO.getAttach(), OrderPaymentAttachDTO.class);
        if(attachDTO == null || NumberUtil.isNullOrNegativeZero(attachDTO.getBatchNo()) || CollectionUtils.isEmpty(attachDTO.getOrderIds())) {
            logger.error("订单支付回调消息，透传消息反序列化后数据错误：" + jsonMessage);
            return;
        }

        //从会员服务查询用户信息
        MemberUserDTO memberUserDTO = NumberUtil.isNullOrZero(attachDTO.getUserId()) ? new MemberUserDTO() : memberFeignService.findUserDetail(attachDTO.getUserId());

        Wrapper<Void> result = baseOrderPaymentService.buyerPayCallback(attachDTO.getOrderIds(), attachDTO.getBatchNo(), attachDTO.getTradeNo(), callbackStatusEnum, memberUserDTO.getUserName(), memberUserDTO.getOrganizationName(), memberUserDTO.getJobTitle());
        if(result.getCode() != ResponseCode.SUCCESS.getCode()) {
            logger.error("订单支付mq回调处理错误，code:" + result.getCode() + ", msg:" + result.getMessage());
        }
    }

    /**
     * 订单发送赠品优惠券
     * @param jsonMessage
     */
    @Override
    public void sengCoupon(String jsonMessage) {
        MarketingQueueDTO marketingQueueDTO = new MarketingQueueDTO();
        marketingQueueDTO.setType(MkQueueMessageTypeEnum.GIFT_COUPON.getCode());
        marketingQueueDTO.setMessage(jsonMessage);
        //转换为Json字符串，推送至消息队列
        String json = JSONUtil.toJsonStr(marketingQueueDTO);
        logger.info("订单发送赠品优惠券消息:{}",json);
        rabbitTemplate.convertAndSend(MkFeignConstants.MK_NORMAL_EXCHANGE, MkFeignConstants.MK_NORMAL_ROUTING_KEY, json);
    }
}
